package com.raos.example.memory;

import com.raos.example.service.Assistant;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 会话记忆聊天 webapi
 *
 * @author raos
 * @date 2025-03-20
 */
@Slf4j
@RequestMapping("/api/mem")
@RestController
public class MemoryAPI {

    @Resource(name = "qwenChatModel")
    ChatLanguageModel chatLanguageModel;

    /**
     * 聊天记录存储对象（初始化window聊天存储消息数）
     */
    private final ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);

    /**
     * 基于langchain4j 底层API的会话记忆聊天
     */
    @GetMapping("/low/chat")
    public String chat(@RequestParam(value = "message") String message) {
        log.info("message=[{}]", message);
        // 添加用户聊内容
        chatMemory.add(UserMessage.userMessage(message));
        // 获取全部聊天内容
        ChatResponse response = chatLanguageModel.chat(chatMemory.messages());
        // 添加AI的回复内容
        chatMemory.add(response.aiMessage());
        String chat = response.aiMessage().text();
        log.info("chat=[{}]", chat);
        return chat;
    }

/*
问答内容1：springboot的核心内容是哪些
会话记忆聊天回复1：
Spring Boot 是一个用于快速构建生产级别的 Spring 应用程序的框架，其核心内容主要包括以下几个方面：

### 1. **自动配置（Auto-Configuration）**
   - Spring Boot 通过自动配置机制，根据项目的依赖和环境自动配置 Spring 应用。这意味着开发者无需手动配置大量的 XML 或 Java 配置，Spring Boot 会根据类路径中的 jar 包、已定义的 Bean 以及其他设置来自动配置应用程序。
   - 例如，如果项目中引入了 `spring-boot-starter-data-jpa`，Spring Boot 会自动配置数据源、JPA 等组件。

### 2. **起步依赖（Starter Dependencies）**
   - Spring Boot 提供了一系列的“起步依赖”（Starter Dependencies），这些依赖项将常用的库和框架打包在一起，简化了依赖管理。
   - 例如，`spring-boot-starter-web` 包含了 Spring MVC、Tomcat 等 Web 开发所需的依赖，开发者只需要引入这个起步依赖，就可以快速构建一个 Web 应用。

### 3. **嵌入式服务器（Embedded Server）**
   - Spring Boot 内置了 Tomcat、Jetty、Undertow 等嵌入式服务器，开发者无需单独部署应用服务器，可以直接运行打包好的 JAR 文件来启动应用。
   - 这使得 Spring Boot 应用可以独立运行，简化了部署流程。

### 4. **外部化配置（Externalized Configuration）**
   - Spring Boot 支持通过多种方式（如 `application.properties`、`application.yml`、环境变量、命令行参数等）来配置应用程序。
   - 配置优先级清晰，开发者可以通过外部化配置来灵活地管理应用的不同环境（如开发、测试、生产）。

### 5. **Actuator**
   - Spring Boot Actuator 提供了生产级别的监控和管理功能，如健康检查、指标收集、环境信息、日志管理等。
   - 通过 Actuator，开发者可以方便地监控和管理 Spring Boot 应用的运行状态。

### 6. **Spring Boot CLI（Command Line Interface）**
   - Spring Boot 提供了一个命令行工具（CLI），允许开发者通过 Groovy 脚本快速创建和运行 Spring Boot 应用。
   - CLI 特别适合快速原型开发和简单应用的构建。

### 7. **Spring Boot DevTools**
   - Spring Boot DevTools 提供了开发时的热部署功能，开发者可以在代码修改后自动重启应用，提高开发效率。
   - 它还提供了自动重启、LiveReload 等开发工具，帮助开发者快速迭代应用。

### 8. **Spring Boot Testing**
   - Spring Boot 提供了强大的测试支持，包括单元测试、集成测试、端到端测试等。
   - 通过 `@SpringBootTest` 注解，开发者可以轻松地测试 Spring Boot 应用的各个层次。

### 9. **Spring Boot 的 Actuator 端点**
   - Spring Boot Actuator 提供了一系列的 HTTP 端点（如 `/health`、`/metrics`、`/info` 等），用于监控和管理应用。
   - 这些端点可以帮助开发者实时监控应用的运行状态，进行故障排查和性能优化。

### 10. **Spring Boot 的日志管理**
   - Spring Boot 默认集成了 Logback 日志框架，并提供了灵活的日志配置选项。
   - 开发者可以通过 `application.properties` 或 `application.yml` 来配置日志级别、输出格式等。

### 11. **Spring Boot 的国际化支持**
   - Spring Boot 提供了对国际化的支持，开发者可以通过配置文件或数据库来管理不同语言的资源文件。
   - 通过 `MessageSource`，开发者可以轻松实现应用程序的多语言支持。

### 12. **Spring Boot 的安全支持**
   - Spring Boot 与 Spring Security 紧密集成，提供了强大的安全功能，如身份验证、授权、CSRF 防护等。
   - 开发者可以通过简单的配置来保护应用程序的各个部分。

### 13. **Spring Boot 的缓存支持**
   - Spring Boot 提供了对多种缓存框架（如 EhCache、Redis、Caffeine 等）的支持，开发者可以通过注解（如 `@Cacheable`）来轻松实现缓存功能。

### 14. **Spring Boot 的数据库访问支持**
   - Spring Boot 提供了对 JPA、MyBatis、JDBC 等数据库访问框架的支持，开发者可以通过起步依赖快速集成数据库访问功能。
   - 通过自动配置，Spring Boot 可以自动配置数据源、事务管理等组件。

### 15. **Spring Boot 的消息队列支持**
   - Spring Boot 提供了对消息队列（如 RabbitMQ、Kafka）的支持，开发者可以通过起步依赖快速集成消息队列功能。

### 16. **Spring Boot 的模板引擎支持**
   - Spring Boot 支持多种模板引擎（如 Thymeleaf、Freemarker、Mustache 等），开发者可以通过起步依赖快速集成模板引擎。

### 17. **Spring Boot 的 RESTful 支持**
   - Spring Boot 提供了对 RESTful Web 服务的支持，开发者可以通过 `@RestController` 注解轻松构建 RESTful API。

### 18. **Spring Boot 的错误处理**
   - Spring Boot 提供了统一的错误处理机制，开发者可以通过 `@ControllerAdvice` 或 `@ExceptionHandler` 来处理全局或局部的异常。

### 19. **Spring Boot 的跨域支持**
   - Spring Boot 提供了对跨域请求（CORS）的支持，开发者可以通过简单的配置来处理跨域请求。

### 20. **Spring Boot 的配置文件**
   - Spring Boot 支持多种配置文件格式（如 properties、YAML），并提供了灵活的配置管理机制。
   - 开发者可以通过 `@ConfigurationProperties` 注解将配置属性绑定到 Java 对象中。

### 总结
Spring Boot 的核心内容主要围绕简化 Spring 应用的开发、配置和部署展开，通过自动配置、起步依赖、嵌入式服务器等特性，极大地提高了开发效率。同时，Spring Boot 还提供了丰富的生产级功能，如监控、安全、缓存等，帮助开发者构建高质量的应用程序。

问答内容2：上述内容，你认为那部分最重要？
会话记忆聊天回复2：
在 Spring Boot 的核心内容中，**自动配置（Auto-Configuration）** 和 **起步依赖（Starter Dependencies）** 是最重要的部分，因为它们是 Spring Boot 的核心设计理念和最大优势所在。以下是对这两部分重要性的详细分析：

---

### **1. 自动配置（Auto-Configuration）**
自动配置是 Spring Boot 的核心特性之一，它极大地简化了 Spring 应用的开发流程。以下是其重要性：

- **减少手动配置**：Spring Boot 根据项目的依赖和环境自动配置 Spring 应用，开发者无需手动编写大量的 XML 或 Java 配置。
- **智能推断**：Spring Boot 会根据类路径中的 jar 包、已定义的 Bean 以及其他设置，智能地推断出应用的配置需求。
- **灵活性**：开发者可以通过自定义配置或排除某些自动配置来覆盖默认行为，满足特定需求。
- **提高开发效率**：自动配置让开发者可以专注于业务逻辑，而不用花费大量时间在繁琐的配置上。

**总结**：自动配置是 Spring Boot 的核心竞争力，它让 Spring 应用开发变得简单、快速和高效。

---

### **2. 起步依赖（Starter Dependencies）**
起步依赖是 Spring Boot 的另一个核心特性，它通过预定义的依赖包简化了项目构建和依赖管理。以下是其重要性：

- **简化依赖管理**：起步依赖将常用的库和框架打包在一起，开发者只需要引入一个起步依赖，就可以获取所需的所有相关依赖。
- **标准化配置**：起步依赖不仅管理依赖，还提供了标准的配置，避免了版本冲突和依赖不一致的问题。
- **快速上手**：通过起步依赖，开发者可以快速构建特定类型的应用（如 Web 应用、数据访问应用、消息队列应用等），降低了入门门槛。
- **生态整合**：Spring Boot 的起步依赖覆盖了 Spring 生态中的大多数组件（如 Spring MVC、Spring Data JPA、Spring Security 等），并与第三方库（如 Thymeleaf、Redis、Kafka 等）无缝集成。

**总结**：起步依赖是 Spring Boot 快速构建应用的基础，它让依赖管理变得简单、高效，并加速了开发流程。

---

### **为什么这两部分最重要？**
自动配置和起步依赖共同构成了 Spring Boot 的核心设计理念：**“约定优于配置”**（Convention Over Configuration）。它们让开发者能够以最少的配置和最快的速度构建高质量的 Spring 应用。如果没有这两部分，Spring Boot 就无法实现其简化开发流程、提高开发效率的目标。

---

### **其他重要内容**
虽然自动配置和起步依赖是最核心的部分，但以下内容在实际开发中也非常重要：

1. **嵌入式服务器（Embedded Server）**：简化了应用的部署和运行。
2. **外部化配置（Externalized Configuration）**：提供了灵活的环境管理和配置管理。
3. **Actuator**：为生产环境的监控和管理提供了强大支持。
4. **Spring Boot DevTools**：提高了开发时的迭代效率。

---

### **总结**
**自动配置**和**起步依赖**是 Spring Boot 最重要的核心内容，它们让 Spring 应用开发变得简单、快速和高效。其他特性（如嵌入式服务器、Actuator、外部化配置等）则在此基础上进一步提升了 Spring Boot 的实用性和生产级别能力。

*/

    @Resource(name = "assistant")
    Assistant assistant;

    /**
     * 基于langchain4j 高层API的会话记忆聊天（带会话隔离-基础版）
     */
    @GetMapping("/high/chat")
    public String highChat(@RequestParam(value = "memoryId") String memoryId,
                           @RequestParam(value = "message") String message) {
        log.info("memoryId=[{}]，message=[{}]", memoryId, message);
        String chat = assistant.chat(memoryId, message);
        log.info("chat=[{}]", chat);
        return chat;
    }

/*
问答内容1：记住我的名字，我叫宇枫
会话记忆聊天回复1：好的，宇枫，我已经记住你的名字了！如果有任何问题或需要帮助的地方，随时告诉我，我会尽力为你解答。😊

问答内容2：请问我叫什么
会话记忆聊天回复2：你叫**宇枫**。我已经记住了你的名字！有什么我可以帮你的吗，宇枫？ 😊
*/

    /**
     * 会话隔离存储容器
     */
    private final Map<String, ChatMemory> chatMemoryMap = new ConcurrentHashMap<>();

    /**
     * 基于langchain4j 高层API的会话记忆聊天（带会话隔离-进阶版）
     */
    @GetMapping("/high/chat2")
    public String highChat2(@RequestParam(value = "memoryId") String memoryId,
                            @RequestParam(value = "message") String message) {
        log.info("memoryId=[{}]，message=[{}]", memoryId, message);
        if (!chatMemoryMap.containsKey(memoryId)) {
            MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.withMaxMessages(10);
            messageWindowChatMemory.add(UserMessage.userMessage(message));
            chatMemoryMap.put(memoryId, messageWindowChatMemory);
        }
        // 获取会话记忆隔离容器获取前面的会话内容
        List<ChatMessage> messages = chatMemoryMap.get(memoryId).messages();
        ChatResponse chatResponse = chatLanguageModel.chat(messages);
        // 将AI回复存入会话隔离容器
        chatMemoryMap.get(memoryId).add(chatResponse.aiMessage());
        String chat = chatResponse.aiMessage().text();
        log.info("chat=[{}]", chat);
        return chat;
    }

/*
问答内容1：记住我的名字，我叫宇枫16号
会话记忆聊天回复1：好的，我已经记住了你的名字，宇枫16号！如果你有任何问题或需要帮助，随时告诉我！😊

问答内容2：请问我叫什么
会话记忆聊天回复2：好的，我已经记住了，宇枫16号！如果你有任何问题或需要帮助，随时告诉我。😊
*/

}
